package solver

func (g *Grid) CalcCellFreqs(house *House) (cellFreqs [9]int, candidateCells [9][9]bool) {
	for i, cell := range house.cells {
		if cell.value != 0 {
			continue
		}
		for j, candidate := range cell.candidates {
			if candidate {
				candidateCells[j][i] = true
			}
		}
	}
	for i := 0; i < 9; i++ {
		for j := 0; j < 9; j++ {
			if candidateCells[i][j] {
				cellFreqs[i]++
			}

		}
	}
	return
}

func (g *Grid) HiddenPairHouse(house *House) (used bool) {
	cellFreqs, candidateCells := g.CalcCellFreqs(house)
	pairedDigits := []int{}
	for i := 0; i < 9; i++ {
		if cellFreqs[i] == 2 {
			pairedDigits = append(pairedDigits, i)
		}
	}
	size := len(pairedDigits)
	if size < 2 {
		return false
	}
	for i := 0; i < size-1; i++ {
		for j := i + 1; j < size; j++ {
			d1, d2 := pairedDigits[i], pairedDigits[j]
			if candidateCells[d1] == candidateCells[d2] {
				for k := 0; k < 9; k++ {
					if candidateCells[d1][k] {
						used = hiddenPairSetFalse(d1, d2, house.cells[k])
					}
				}
			}
		}
	}
	return
}

func hiddenPairSetFalse(d1 int, d2 int, cell *Cell) (used bool) {
	for i := 0; i < 9; i++ {
		if i == d1 || i == d2 {
			continue
		}
		if cell.removeCandidate(uint8(i) + 1) {
			used = true
		}
	}
	return
}

func (g *Grid) HiddenPair() (used bool) {
	for _, box := range g.boxes {
		if g.HiddenPairHouse(&box) {
			used = true
		}
	}

	for _, row := range g.rows {
		if g.HiddenPairHouse(&row) {
			used = true
		}
	}

	for _, col := range g.cols {
		if g.HiddenPairHouse(&col) {
			used = true
		}
	}
	return
}

func clearNakedPairHouse(house *House, i int, j int) (used bool) {
	values := []uint8{}
	for k, candidate := range house.cells[i].candidates {
		if candidate {
			values = append(values, uint8(k)+1)
		}
	}
	for k := 0; k < 9; k++ {
		if k == i || k == j {
			continue
		}
		for _, value := range values {
			if house.cells[k].removeCandidate(value) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) NakedPairHouse(house *House) (used bool) {
	for i := 0; i < 8; i++ {
		if house.cells[i].value != 0 || house.cells[i].candidateCount != 2 {
			continue
		}
		for j := i + 1; j < 9; j++ {
			if house.cells[j].value != 0 || house.cells[j].candidateCount != 2 {
				continue
			}
			if house.cells[i].candidates == house.cells[j].candidates {
				if clearNakedPairHouse(house, i, j) {
					used = true
				}
			}
		}
	}
	return
}

func (g *Grid) NakedPair() (used bool) {
	for _, box := range g.boxes {
		if g.NakedPairHouse(&box) {
			used = true
		}
	}

	for _, row := range g.rows {
		if g.NakedPairHouse(&row) {
			used = true
		}
	}

	for _, col := range g.cols {
		if g.NakedPairHouse(&col) {
			used = true
		}
	}
	return
}

func (g *Grid) HiddenTripleHouse(house *House) (used bool) {
	cellFreqs, candidateCells := g.CalcCellFreqs(house)
	triples := calcTriples(cellFreqs)
	for _, triple := range triples {
		sum := [9]bool{}
		//{true, true, true, true, true, true, true, true, true }
		for _, v := range triple {
			for i := 0; i < 9; i++ {
				sum[i] = sum[i] || candidateCells[v][i]
			}
		}
		count := 0
		clearupCells := []*Cell{}
		for i, elem := range sum {
			if elem {
				count++
				clearupCells = append(clearupCells, house.cells[i])
			}
		}
		if count != 3 {
			continue
		}
		//fmt.Println(count, triple)
		for _, cell := range clearupCells {
			if cell.removeCandidateBut(triple[:]) {
				used = true
			}
		}

	}
	return
}

func contains(vs []uint8, v uint8) bool {
	for _, e := range vs {
		if e == v {
			return true
		}
	}
	return false
}

func (c *Cell) removeCandidateBut(vs []uint8) (used bool) {
	if c.value != 0 {
		return false
	}
	for i := uint8(0); i < 9; i++ {
		if contains(vs, i) || !c.candidates[i] {
			continue
		}
		c.candidates[i] = false
		c.candidateCount--
		used = true
	}
	return
}

func calcTriples(cellFreqs [9]int) [][3]uint8 {
	vs := []uint8{}
	for v, freq := range cellFreqs {
		if freq != 0 && freq <= 3 {
			vs = append(vs, uint8(v))
		}
	}
	size := len(vs)
	//fmt.Println(size, vs)
	comb := [][3]uint8{}
	if size < 3 {
		return comb
	}
	for i := 0; i < size-2; i++ {
		for j := i + 1; j < size-1; j++ {
			for k := j + 1; k < size; k++ {
				comb = append(comb, [3]uint8{vs[i], vs[j], vs[k]})
			}
		}
	}
	return comb
}

func (g *Grid) HiddenTriple() (used bool) {
	for _, box := range g.boxes {
		if g.HiddenTripleHouse(&box) {
			used = true
		}
	}

	for _, row := range g.rows {
		if g.HiddenTripleHouse(&row) {
			used = true
		}
	}

	for _, col := range g.cols {
		if g.HiddenTripleHouse(&col) {
			used = true
		}
	}
	return
}

func (g *Grid) NakedTripleHouse(house *House) (used bool) {
	return
	for i := 0; i < 8; i++ {
		if house.cells[i].value != 0 || house.cells[i].candidateCount != 2 {
			continue
		}
		for j := i + 1; j < 9; j++ {
			if house.cells[j].value != 0 || house.cells[j].candidateCount != 2 {
				continue
			}
			if house.cells[i].candidates == house.cells[j].candidates {
				if clearNakedPairHouse(house, i, j) {
					used = true
				}
			}
		}
	}
	return
}


